// UpdaterService.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.ApplicationBlocks.Updater;
using Microsoft.Practices.ServiceLocation;
using Supremacy.Annotations;
using Supremacy.Client;
using Supremacy.Client.Commands;
using Supremacy.Client.Dialogs;
using Supremacy.Resources;

namespace Supremacy.Updater
{
    internal class UpdaterService : IUpdaterService
    {
        private const string ApplicationID = "StarTrekSupremacy";

        private readonly object _updaterLock;
        private readonly IClientApplication _clientApplication;
        private readonly IDispatcherService _dispatcherService;
        private readonly CancellationTokenSource _cancellationTokenSource;

        private ApplicationUpdaterManager _updateManager;
        private Task _updaterTask;

        public UpdaterService(
            [NotNull] IClientApplication clientApplication,
            [NotNull] IDispatcherService dispatcherService)
        {
            if (clientApplication == null)
                throw new ArgumentNullException("clientApplication");
            if (dispatcherService == null)
                throw new ArgumentNullException("dispatcherService");
            _clientApplication = clientApplication;
            _dispatcherService = dispatcherService;
            _updaterLock = new object();
            _cancellationTokenSource = new CancellationTokenSource();
        }

        private void CheckForUpdatesCore()
        {
            if (!NetworkInterface.GetIsNetworkAvailable())
                return;

            EnsureUpdater();

            var manifests = _updateManager.CheckForUpdates();

            _dispatcherService.InvokeAsync(
                (Action<Manifest[]>)CheckForUpdatesAsyncCallback,
                new[] { manifests });
        }

        protected void CheckForUpdatesAsyncCallback(params Manifest[] manifests)
        {
            var clientContext = ServiceLocator.Current.GetInstance<IClientContext>();

            CommandManager.InvalidateRequerySuggested();

            if (manifests.Length <= 0)
                return;

            // Ask the user if she wants to apply the updates.
            if (MessageDialog.Show(
                    ResourceManager.GetString("UPDATE_AVAILABLE_HEADER"),
                    String.Format(
                        ResourceManager.GetString("UPDATE_AVAILABLE_MESSAGE"),
                        ResourceManager.GetString("GAME_NAME")),
                    MessageDialogButtons.YesNo) != MessageDialogResult.Yes)
            {
                return;
            }

            // If there is a game in play, give the player an opportunity to save her game.
            if (clientContext.IsGameInPlay)
            {
                if (MessageDialog.Show(
                        ResourceManager.GetString("UPDATE_PROMPT_SAVE_GAME_HEADER"),
                        ResourceManager.GetString("UPDATE_PROMPT_SAVE_GAME_MESSAGE"),
                        MessageDialogButtons.YesNo) == MessageDialogResult.Yes)
                {
                    ClientCommands.ShowSaveGameDialog.Execute(null);
                }
                ClientCommands.EndGame.Execute(false);
            }

            foreach (var m in manifests)
                m.Apply = true;

            var updaterWindow = new UpdaterDialog(_updateManager, manifests);

            updaterWindow.ShowDialog();

            if (updaterWindow.DialogResult.HasValue && updaterWindow.DialogResult.Value)
                Environment.Exit(0);
        }

        private void EnsureUpdater()
        {
            lock (_updaterLock)
            {
                if (_updateManager == null)
                    _updateManager = ApplicationUpdaterManager.GetUpdater();
            }
        }

        #region Implementation of IUpdaterService
        public void CheckForUpdates()
        {
            lock (_updaterLock)
            {
                if ((_updaterTask != null) && !_updaterTask.IsCanceled && !_updaterTask.IsCompleted)
                    return;
                _updaterTask = Task.Factory.StartNew(CheckForUpdatesCore).ContinueWith(
                    task =>
                    {
                        lock (_updaterLock)
                        {
                            _updaterTask = null;
                        }
                    });
            }
        }

        public bool CanCheckForUpdates
        {
            get
            {
                if (_clientApplication.IsShuttingDown)
                    return false;

                if (!NetworkInterface.GetIsNetworkAvailable())
                    return false;

                if (_clientApplication.CommandLineArguments.DisableUpdates)
                    return false;

                lock (_updaterLock)
                {
                    return ((_updaterTask == null) || _updaterTask.IsCanceled || _updaterTask.IsCompleted);
                }
            }
        }

        public void CancelUpdateCheck()
        {
            lock (_updaterLock)
            {
                if ((_updaterTask == null) || _updaterTask.IsCanceled || _updaterTask.IsCompleted)
                    return;
                try { _cancellationTokenSource.Cancel(); }
                catch {}
            }
        }

        public void ClearUpdaterRegistry()
        {
            ApplicationUpdaterManager.ManifestManager.ClearRegistry(ApplicationID);
        }
        #endregion
    }
}